/*
    Malware Persistence Series.
     => This program reads and shows details from a specific Windows registry key, focusing on COM objects related to UAC settings.
     => Registry key: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList
    
    By @5mukx
*/

use std::ptr;
use winapi::shared::minwindef::{DWORD, HKEY};
use winapi::um::winnt::KEY_READ;
use winapi::um::winreg::{
    HKEY_LOCAL_MACHINE, RegCloseKey, RegOpenKeyExW, RegEnumValueW,
};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut hkey: HKEY = ptr::null_mut();
    let key_path: Vec<u16> = "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\UAC\\COMAutoApprovalList\0"
        .encode_utf16()
        .collect();

    unsafe {
        match RegOpenKeyExW(
            HKEY_LOCAL_MACHINE,
            key_path.as_ptr(),
            0,
            KEY_READ,
            &mut hkey,
        ) {
            0 => {
                // enumerate all values in the key and print it out 
                let mut index: DWORD = 0;
                let mut value_name = [0u16; 256];
                let mut name_len = value_name.len() as DWORD;
                let mut value_type = 0;
                let mut data = [0u8; 4096];
                let mut data_len = data.len() as DWORD;

                loop {
                    match RegEnumValueW(
                        hkey,
                        index,
                        value_name.as_mut_ptr(),
                        &mut name_len,
                        ptr::null_mut(),
                        &mut value_type,
                        data.as_mut_ptr() as *mut u8,
                        &mut data_len,
                    ) {
                        /*ERROR_SUCCESS*/0 => {
                            let name = String::from_utf16_lossy(&value_name[..name_len as usize]);
                            println!("Value name: {}", name);

                            match value_type {
                                winapi::um::winnt::REG_SZ => {
                                    let data_str= {
                                        String::from_utf16_lossy(
                                            &data[..data_len as usize]
                                            .chunks_exact(2)
                                            .map(|chunk| u16::from_le_bytes([chunk[0], chunk[1]]))
                                            .collect::<Vec<u16>>()
                                        )
                                    };
                                    println!("  Data: {}", data_str);    
                                }

                                winapi::um::winnt::REG_DWORD => {
                                    if data_len == 4 {
                                        let dword_value = u32::from_le_bytes(data[..4].try_into().unwrap());
                                        println!("  Data (REG_DWORD): {}", dword_value);
                                    } else{
                                        println!("  Data (REG_DWORD): Unexpected size");
                                    }
                                }
                                _ => println!("  Data: Unknown type {}", value_type),
                            }

                            index += 1;
                            name_len = value_name.len() as DWORD;
                            data_len = data.len() as DWORD;
                        }
                        /*ERROR_NO_MORE_ITEMS*/ 259 => break,
                        _ => {
                            println!("Failed to enumerate values");
                            break;
                        }
                    }
                }
                RegCloseKey(hkey);
            }
            _ => println!("Failed to open registry key"),
        }
    }
    Ok(())
}
